package com.ogilvy.audi.dao.hibernate;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.ogilvy.audi.dao.GenericDao;
import org.hibernate.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

/**
 * This class serves as the Base class for all other DAOs - namely to hold
 * common CRUD methods that they might all use. You should only need to extend
 * this class when your require custom CRUD logic.
 * <p/>
 * <p>To register this class in your Spring context file, use the following XML.
 * <pre>
 *      &lt;bean id="fooDao" class="com.ogilvy.audi.dao.hibernate.GenericDaoHibernate"&gt;
 *          &lt;constructor-arg value="com.ogilvy.audi.model.Foo"/&gt;
 *      &lt;/bean&gt;
 * </pre>
 *
 * @author <a href="mailto:bwnoll@gmail.com">Bryan Noll</a>
 * @param <T> a type variable
 * @param <PK> the primary key for that type
 */
public class GenericDaoHibernate<T, PK extends Serializable> implements GenericDao<T, PK> {
    /**
     * Log variable for all child classes. Uses LogFactory.getLog(getClass()) from Commons Logging
     */
    protected final Log log = LogFactory.getLog(getClass());
    private Class<T> persistentClass;
    private HibernateTemplate hibernateTemplate;
    private SessionFactory sessionFactory;

    /**
     * Constructor that takes in a class to see which type of entity to persist.
     * Use this constructor when subclassing.
     *
     * @param persistentClass the class type you'd like to persist
     */
    public GenericDaoHibernate(final Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }

    /**
     * Constructor that takes in a class and sessionFactory for easy creation of DAO.
     *
     * @param persistentClass the class type you'd like to persist
     * @param sessionFactory  the pre-configured Hibernate SessionFactory
     */
    public GenericDaoHibernate(final Class<T> persistentClass, SessionFactory sessionFactory) {
        this.persistentClass = persistentClass;
        this.sessionFactory = sessionFactory;
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    public HibernateTemplate getHibernateTemplate() {
        return this.hibernateTemplate;
    }

    public SessionFactory getSessionFactory() {
        return this.sessionFactory;
    }

    @Autowired
    @Required
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<T> getAll() {
        return hibernateTemplate.loadAll(this.persistentClass);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<T> getAllDistinct() {
        Collection result = new LinkedHashSet(getAll());
        return new ArrayList(result);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public T get(PK id) {
        T entity = (T) hibernateTemplate.get(this.persistentClass, id);

        if (entity == null) {
            log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found...");
            throw new ObjectRetrievalFailureException(this.persistentClass, id);
        }

        return entity;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public boolean exists(PK id) {
        T entity = (T) hibernateTemplate.get(this.persistentClass, id);
        return entity != null;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public T save(T object) {
        return (T) hibernateTemplate.merge(object);
    }

    /**
     * {@inheritDoc}
     */
    public void remove(PK id) {
        hibernateTemplate.delete(this.get(id));
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<T> findByNamedQuery(String queryName, Map<String, Object> queryParams) {
        String[] params = new String[queryParams.size()];
        Object[] values = new Object[queryParams.size()];
        
        int index = 0;
        for (String s : queryParams.keySet()) {
            params[index] = s;
            values[index++] = queryParams.get(s);
        }

        return hibernateTemplate.findByNamedQueryAndNamedParam(queryName, params, values);
    }

           public synchronized  void batchInsertSet(final Set<T> sets) {
           log.debug("Entering batchInsertSet method");
          getHibernateTemplate().saveOrUpdateAll(sets);
       }

    public synchronized  void delete(Object entity) {
           log.debug("Entering batchInsertSet method");
          getHibernateTemplate().delete(entity);
       }





       public List<T> getRecords(final int firstRecord,final  int page_size, final String sql,final Collection<String> dateSet) {
          	HibernateCallback callback = new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
                log.debug("The getRecords sql is "+sql);
				Query q = session.createQuery(sql);
	             q.setFirstResult(firstRecord);
                q.setMaxResults(page_size);

                if(null!=dateSet){
                    q= putDateParameter(q,dateSet);

                }

				List<T> objs = (List<T>)q.list();
				return objs;
			}
		};
		return (List<T>) getHibernateTemplate().execute(callback);

    }

public Integer getRecordSizeBySql(final String sql,final Collection<String> dateSet) {
           log.debug("The recordSize sql is "+sql);
		HibernateCallback callback = new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Query sqlQuery = session.createQuery(sql);

                   if(null!=dateSet){
                       sqlQuery= putDateParameter(sqlQuery,dateSet);
                }

				List results = sqlQuery.list();

			    return results;
			}
		};
		List sizes= (List) getHibernateTemplate().execute(callback);
           if(null==sizes||sizes.isEmpty())
             return 0;
        Long size=(Long)sizes.get(0);
		return size.intValue();
	}




  	public Integer getRecordSizeBySql(final String sql,final String sDate,final String eDate) {
           log.debug("The recordSize sql is "+sql);
		HibernateCallback callback = new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Query sqlQuery = session.createQuery(sql);
                   if(StringUtils.isNotBlank(sDate)){
                      java.sql.Date minDate= java.sql.Date.valueOf(sDate);
                          sqlQuery.setParameter(0,minDate);
                    }
                    if(StringUtils.isNotBlank(eDate)){
                      java.sql.Date maxDate= java.sql.Date.valueOf(eDate);
                          sqlQuery.setParameter(1,maxDate);
                    }
				List results = sqlQuery.list();

			    return results;
			}
		};
		List sizes= (List) getHibernateTemplate().execute(callback);
           if(null==sizes||sizes.isEmpty())
             return 0;
        Long size=(Long)sizes.get(0);
		return size.intValue();
	}


    public List<T> getRecords(final String sql,final Collection dateSet) {
        log.debug("the hql is"+sql);
        	HibernateCallback callback = new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Query q = session.createQuery(sql);
                    if(null!=dateSet){
                      q= putDateParameter(q,dateSet);
                     }

				List<T> objs = (List<T>)q.list();
                //session.flush();
                //session.close();

				return objs;
			}
		};
		return (List<T>) getHibernateTemplate().execute(callback);
    }

    public List<T> getAllRecords(final String sql) {
        	HibernateCallback callback = new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Query q = session.createQuery(sql);
				List<T> objs = (List<T>)q.list();
                     //session.flush();
                //session.close();
				return objs;
			}
		};

		return (List<T>) getHibernateTemplate().execute(callback);
    }



        public  void  executeByNativeSQL(final String nativeSql){
          log.debug("the nativeSql is"+nativeSql);
           	HibernateCallback callback = new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
                    Connection conn = session.connection();
                     conn.prepareStatement(nativeSql).execute();
                        conn.commit();
                           conn.close();
                        session.close();
                     return   null;
			}

		};
		getHibernateTemplate().execute(callback);
    }







   public int batchUpdate(final String hql,final Collection<String> dateSet){

         	HibernateCallback callback = new HibernateCallback() {
                public Object doInHibernate(Session session)
                        throws HibernateException, SQLException {
                    //Transaction tx = session.beginTransaction();
                Query q=   session.createQuery(hql);
                     if (null!=dateSet){
                             q = putDateParameter(q,dateSet);
                     }
                       int updatedEntities =q.executeUpdate();
                        //tx.commit();
                      //session.close();
                    return  updatedEntities;
            }
             };
       int sizes= (Integer) getHibernateTemplate().execute(callback);
            return sizes;
   }



        public Integer getCountGroupSize(final String hql,final Collection<String> dateSet){
          	HibernateCallback callback = new HibernateCallback() {
                  public Object doInHibernate(Session session) throws HibernateException, SQLException {
                      Query q = session.createQuery(hql);
               if(null!=dateSet){
                      q = putDateParameter(q,dateSet);
                }
                          ScrollableResults scr = q.scroll();
                            scr.last();
                     Integer size = scr.getRowNumber();
                       log.debug("The count size is "+size) ;
                      if(size>=0)
                           size=size+1;
                      return size;  //To change body of implemented methods use File | Settings | File Templates.
                  }
              };

           return  (Integer)getHibernateTemplate().execute(callback);

    }



    public int executeHql(final String hql,final java.sql.Date dateTime,final Integer start,final Integer end){
      log.debug("The executeHql  is "+hql);
     HibernateCallback callback = new HibernateCallback() {
                public Object doInHibernate(Session session)
                        throws HibernateException, SQLException {
                    //Transaction tx = session.beginTransaction();
                             Query sqlQuery =    session.createQuery(hql);
                               if(null!=dateTime)
                                 sqlQuery.setParameter(0,dateTime);
                           if(null!=start&&0!=start){
                                 sqlQuery.setFirstResult(start) ;
                           }
                          if(null!=end&&0!=end){
                                 sqlQuery.setMaxResults(end) ;
                           }
                       int updatedEntities =sqlQuery.executeUpdate();
                        //tx.commit();
                      //session.flush();
                    return  updatedEntities;
            }
             };
       int sizes= (Integer) getHibernateTemplate().execute(callback);
            return sizes;
  }






    /**
     * 设置 时间参数
     * @param sqlQuery
     * @param dateSet
     * @return
     */
    private Query putDateParameter(Query sqlQuery,final Collection<String> dateSet){
        List list=new ArrayList(dateSet);
          for(int k=0;k<list.size();k++){
           String s_date=(String)list.get(k);
                 java.sql.Date minDate= java.sql.Date.valueOf(s_date);
              sqlQuery.setParameter(k,minDate);
          }

        return sqlQuery;
    }
}
